iT邦幫忙

2022 iThome 鐵人賽

DAY 9
0
Modern Web

擁抱 .Net Core系列 第 9

[Day9] 補坑之談談 .Net core 的相依性注入實作

  • 分享至 

  • xImage
  •  

因為今天比較忙,剩沒多少時間,補上之前欠的內容 /images/emoticon/emoticon06.gif

回顧一下

我們在 [Day4] .Net Core 中的相依性注入 - 1[Day5] .Net Core 中的相依性注入 - 2 中有提到一些比較重要的觀念與物件

  • IServiceCollection
  • IServiceProvider
  • IServiceScope
  • IServiceProviderFactory
  • ServiceDescriptor

今天來介紹最核心的物件 ServiceProviderEngineServiceProviderEngineScope

ServiceProviderEngine

ServiceProviderEngineScope

ServiceProviderEngine 是最後的提供出執行個體的那個物件,在一個dotnet core的應用程式範圍中他只存在全域唯一的一個,ServiceProviderEngineScope 則是負責管理範圍內生命週期的物件

IServiceProviderEngineScope,節錄幾個方法與屬性

internal sealed class ServiceProviderEngineScope : IServiceScope, IServiceProvider, IAsyncDisposable, IServiceScopeFactory{
    internal IList<object> Disposables{ get; }
    internal Dictionary<ServiceCacheKey, object> ResolvedServices { get; }
    internal ServiceProvider RootProvider { get; }
    public IServiceProvider ServiceProvider => this;
    public IServiceScope CreateScope() => RootProvider.CreateScope();
    public object GetService(Type serviceType)
    {
        if (_disposed)
        {
            ThrowHelper.ThrowObjectDisposedException();
        }

        return RootProvider.GetService(serviceType, this);
    }
}

我們之前有提過 IServiceProvider 會有一個ResolvedServices 紀錄已建立的物件,
一個 Disposables 存放要Dispose的物件

ServiceProviderEngine 既是 IServiceScope 也是 IServiceScopeFactory
所以該有的他都有
有趣的是他的 ServiceProvider 是指向自己的 所以當對她使用GetService<IServiceProvider>時最後拿到的就是他自己。

ServiceProviderEngine

本身是一個抽象類別,定義了類別如何被實現

internal abstract class ServiceProviderEngine
{
    public abstract Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite);
}

主要有4種實作方式

  • ILEmitServiceProviderEngine: 透過IL Emit的方式提供執行個體
  • RuntimeServiceProviderEngine: 透過反射取得執行個體
  • ExpressionsServiceProviderEngine: 利用Expression Tree來產生執行個體
  • DynamicServiceProviderEngine(CompiledServiceProviderEngine 背後的實作): Dotnet core 預設使用的方式,主要是透過當前的執行序池的請求數量決定要走上面3種的的哪一種,走ILEmitServiceProviderEngineRuntimeServiceProviderEngine 取決於runtime支不支援Reflection Emit

更細節的物件實際被產生就下細看這三個類別了(超難懂我放棄)
這邊只大概提 Runtime的產生實作的作法 (我跟expression 是不熟悉的陌生人,就是不認識XD,懂IL我就不會在這裡了)

RuntimeServiceProviderEngine

主要有幾個步驟,不貼程式了

  1. ServiceDescriptor轉成ServiceCallSite物件,我們在前面有提到ServiceDescriptor
    有幾個屬性ImplementXXX的屬性,會分別對應到不同的ServiceCallSite,以ServiceDescriptor.ImplementationType 為例,當我們使用的這個方式註冊Service時
    Services.AddScope<ISampleService,SampleService>,會建立一個ServiceDescriptor.ImplementationType = Typeof(SampleService)ServiceDescriptor
    他會被解析成一個 ConstructorCallSite 的物件,裡面紀錄了一個這個ImplementType參數最多的建構式的資訊
    (轉成ServiceCallSite所有Engine都會做)
  2. 透過ConstructorInfo.Invoke 的方式建立出執行個體

ConstructorInfo.Invoke Sample

var constructorInfos = typeof(SampleService).GetConstructors().OrderByDescending(x=>x.GetParameters().Length).ToList();
var withConstructorInstance = (ISampleService) constructorInfos[0].Invoke(new object?[]{"Eric"});
var withoutConstructorInstance = (ISampleService) constructorInfos[1].Invoke(null);

withConstructorInstance.SayHello();
withoutConstructorInstance.SayHello();


public interface ISampleService
{
    void SayHello();
}

public class SampleService : ISampleService
{
    private readonly string _a;

    public SampleService()
    {
    }
    
    public SampleService(string a)
    {
        _a = a;
    }

    public void SayHello()
    {
        if (_a is not null)
        {
            Console.WriteLine($"Hello World!, {_a}");
        }
        else
        {
            Console.WriteLine("Hello World!");
        }
    }
}

https://ithelp.ithome.com.tw/upload/images/20220921/20109549BVOCuCfsQt.png

其實裡面還包含了快取,遞迴依賴等處理,太複雜了pass

使用第三方的或自訂的DI Container


上一篇
[Day8] 檔案提供者(檔案系統)
下一篇
[Day10] 談談設定檔(組態) - Configuration - 1
系列文
擁抱 .Net Core30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言